"""笔记管理模块的测试用例

测试笔记CRUD操作、分类标签管理等功能。
"""

import json
import tempfile
from django.test import TestCase, TransactionTestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from django.db import transaction
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken

from .models import Category, Tag, Note, NoteVersion, Attachment
from .serializers import (
    CategorySerializer, TagSerializer, NoteListSerializer,
    NoteDetailSerializer, AttachmentSerializer
)

User = get_user_model()


class CategoryModelTest(TestCase):
    """分类模型测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
    
    def test_create_category(self):
        """测试创建分类"""
        category = Category.objects.create(
            name='技术文档',
            description='技术相关的文档',
            created_by=self.user
        )
        
        self.assertEqual(category.name, '技术文档')
        self.assertEqual(category.slug, '技术文档')
        self.assertEqual(category.created_by, self.user)
        self.assertIsNone(category.parent)
    
    def test_create_subcategory(self):
        """测试创建子分类"""
        parent_category = Category.objects.create(
            name='技术文档',
            created_by=self.user
        )
        
        subcategory = Category.objects.create(
            name='Python',
            parent=parent_category,
            created_by=self.user
        )
        
        self.assertEqual(subcategory.parent, parent_category)
        self.assertIn(subcategory, parent_category.children.all())
    
    def test_category_full_path(self):
        """测试分类完整路径"""
        parent = Category.objects.create(
            name='技术文档',
            created_by=self.user
        )
        
        child = Category.objects.create(
            name='Python',
            parent=parent,
            created_by=self.user
        )
        
        self.assertEqual(child.full_path, '技术文档 > Python')
    
    def test_category_string_representation(self):
        """测试分类字符串表示"""
        category = Category.objects.create(
            name='技术文档',
            created_by=self.user
        )
        
        self.assertEqual(str(category), '技术文档')


class TagModelTest(TestCase):
    """标签模型测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
    
    def test_create_tag(self):
        """测试创建标签"""
        tag = Tag.objects.create(
            name='Python',
            color='#3776ab',
            created_by=self.user
        )
        
        self.assertEqual(tag.name, 'Python')
        self.assertEqual(tag.slug, 'python')
        self.assertEqual(tag.color, '#3776ab')
        self.assertEqual(tag.usage_count, 0)
    
    def test_tag_string_representation(self):
        """测试标签字符串表示"""
        tag = Tag.objects.create(
            name='Python',
            created_by=self.user
        )
        
        self.assertEqual(str(tag), 'Python')


class NoteModelTest(TransactionTestCase):
    """笔记模型测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.category = Category.objects.create(
            name='技术文档',
            created_by=self.user
        )
        
        self.tag = Tag.objects.create(
            name='Python',
            created_by=self.user
        )
    
    def test_create_note(self):
        """测试创建笔记"""
        with transaction.atomic():
            note = Note.objects.create(
                title='Python学习笔记',
                content='# Python基础\n\n这是一篇Python学习笔记。',
                author=self.user,
                category=self.category,
                status='draft'
            )
            note.tags.add(self.tag)
        
        self.assertEqual(note.title, 'Python学习笔记')
        self.assertEqual(note.author, self.user)
        self.assertEqual(note.category, self.category)
        self.assertEqual(note.status, 'draft')
        self.assertIn(self.tag, note.tags.all())
    
    def test_note_auto_slug_generation(self):
        """测试笔记自动生成slug"""
        with transaction.atomic():
            note = Note.objects.create(
                title='Python学习笔记',
                content='测试内容',
                author=self.user
            )
        
        self.assertEqual(note.slug, 'python学习笔记')
    
    def test_note_auto_excerpt_generation(self):
        """测试笔记自动生成摘要"""
        content = '# Python基础\n\n这是一篇关于Python基础知识的学习笔记，包含了变量、函数、类等重要概念。'
        
        with transaction.atomic():
            note = Note.objects.create(
                title='Python学习笔记',
                content=content,
                author=self.user
            )
        
        self.assertTrue(len(note.excerpt) > 0)
        self.assertIn('Python基础', note.excerpt)
    
    def test_note_word_count(self):
        """测试笔记字数统计"""
        content = '这是一篇测试笔记，用于测试字数统计功能。'
        
        with transaction.atomic():
            note = Note.objects.create(
                title='测试笔记',
                content=content,
                author=self.user
            )
        
        self.assertTrue(note.word_count > 0)
    
    def test_note_reading_time(self):
        """测试笔记阅读时间估算"""
        content = '这是一篇测试笔记。' * 100  # 创建较长的内容
        
        with transaction.atomic():
            note = Note.objects.create(
                title='长篇测试笔记',
                content=content,
                author=self.user
            )
        
        self.assertTrue(note.reading_time >= 1)
    
    def test_note_published_status(self):
        """测试笔记发布状态"""
        with transaction.atomic():
            note = Note.objects.create(
                title='测试笔记',
                content='测试内容',
                author=self.user,
                status='published'
            )
        
        self.assertTrue(note.is_published)
        self.assertIsNotNone(note.published_at)


class NoteAPITest(APITestCase):
    """笔记API测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        
        # 创建测试数据
        self.category = Category.objects.create(
            name='技术文档',
            created_by=self.user
        )
        
        self.tag = Tag.objects.create(
            name='Python',
            created_by=self.user
        )
    
    def test_create_note(self):
        """测试创建笔记"""
        note_data = {
            'title': 'Python学习笔记',
            'content': '# Python基础\n\n这是一篇Python学习笔记。',
            'category': self.category.id,
            'tags': [self.tag.id],
            'status': 'draft'
        }
        
        response = self.client.post(
            reverse('notes:note-list'),
            note_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['title'], 'Python学习笔记')
        self.assertTrue(Note.objects.filter(title='Python学习笔记').exists())
    
    def test_list_notes(self):
        """测试获取笔记列表"""
        # 创建测试笔记
        Note.objects.create(
            title='测试笔记1',
            content='内容1',
            author=self.user,
            status='published'
        )
        
        Note.objects.create(
            title='测试笔记2',
            content='内容2',
            author=self.user,
            status='draft'
        )
        
        response = self.client.get(reverse('notes:note-list'))
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 2)
    
    def test_retrieve_note(self):
        """测试获取笔记详情"""
        note = Note.objects.create(
            title='测试笔记',
            content='测试内容',
            author=self.user
        )
        
        response = self.client.get(
            reverse('notes:note-detail', kwargs={'pk': note.id})
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['title'], '测试笔记')
    
    def test_update_note(self):
        """测试更新笔记"""
        note = Note.objects.create(
            title='原标题',
            content='原内容',
            author=self.user
        )
        
        update_data = {
            'title': '更新后的标题',
            'content': '更新后的内容'
        }
        
        response = self.client.patch(
            reverse('notes:note-detail', kwargs={'pk': note.id}),
            update_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['title'], '更新后的标题')
        
        # 验证数据库中的数据已更新
        note.refresh_from_db()
        self.assertEqual(note.title, '更新后的标题')
    
    def test_delete_note(self):
        """测试删除笔记"""
        note = Note.objects.create(
            title='待删除笔记',
            content='内容',
            author=self.user
        )
        
        response = self.client.delete(
            reverse('notes:note-detail', kwargs={'pk': note.id})
        )
        
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(Note.objects.filter(id=note.id).exists())
    
    def test_unauthorized_access(self):
        """测试未授权访问"""
        # 移除认证
        self.client.credentials()
        
        response = self.client.get(reverse('notes:note-list'))
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class CategoryAPITest(APITestCase):
    """分类API测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
    
    def test_create_category(self):
        """测试创建分类"""
        category_data = {
            'name': '技术文档',
            'description': '技术相关的文档分类',
            'color': '#007bff'
        }
        
        response = self.client.post(
            reverse('notes:category-list'),
            category_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], '技术文档')
        self.assertTrue(Category.objects.filter(name='技术文档').exists())
    
    def test_list_categories(self):
        """测试获取分类列表"""
        Category.objects.create(
            name='技术文档',
            created_by=self.user
        )
        
        Category.objects.create(
            name='生活随笔',
            created_by=self.user
        )
        
        response = self.client.get(reverse('notes:category-list'))
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 2)


class TagAPITest(APITestCase):
    """标签API测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
    
    def test_create_tag(self):
        """测试创建标签"""
        tag_data = {
            'name': 'Python',
            'color': '#3776ab'
        }
        
        response = self.client.post(
            reverse('notes:tag-list'),
            tag_data,
            format='json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'Python')
        self.assertTrue(Tag.objects.filter(name='Python').exists())
    
    def test_list_tags(self):
        """测试获取标签列表"""
        Tag.objects.create(
            name='Python',
            created_by=self.user
        )
        
        Tag.objects.create(
            name='Django',
            created_by=self.user
        )
        
        response = self.client.get(reverse('notes:tag-list'))
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 2)


class AttachmentTest(TransactionTestCase):
    """附件测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.note = Note.objects.create(
            title='测试笔记',
            content='测试内容',
            author=self.user
        )
    
    def test_create_attachment(self):
        """测试创建附件"""
        # 创建临时文件
        test_file = SimpleUploadedFile(
            "test.txt",
            b"file_content",
            content_type="text/plain"
        )
        
        with transaction.atomic():
            attachment = Attachment.objects.create(
                note=self.note,
                file=test_file,
                original_name='test.txt',
                file_size=len(test_file.read()),
                uploaded_by=self.user
            )
            test_file.seek(0)  # 重置文件指针
        
        self.assertEqual(attachment.note, self.note)
        self.assertEqual(attachment.original_name, 'test.txt')
        self.assertEqual(attachment.uploaded_by, self.user)
        self.assertTrue(attachment.file_size > 0)


class NoteVersionTest(TransactionTestCase):
    """笔记版本测试"""
    
    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.note = Note.objects.create(
            title='测试笔记',
            content='原始内容',
            author=self.user
        )
    
    def test_create_note_version(self):
        """测试创建笔记版本"""
        with transaction.atomic():
            version = NoteVersion.objects.create(
                note=self.note,
                title='测试笔记 v1',
                content='更新后的内容',
                version_number=1,
                created_by=self.user
            )
        
        self.assertEqual(version.note, self.note)
        self.assertEqual(version.version_number, 1)
        self.assertEqual(version.content, '更新后的内容')
        self.assertEqual(version.created_by, self.user)
